#!/usr/bin/python
# -*- coding: utf-8 -*-

from google.appengine.ext import db


class Field(object):
    """Represents and indexable field on a particular type of model.
    """
    def __init__(self, model_class_name, field_name):
        self.model_class_name = model_class_name
        self.field_name = field_name

    def __unicode__(self):
        return "%s|%s" % (self.model_class_name, self.field_name)

    def __str__(self):
        return unicode(self).encode('ascii', 'replace')

    def __eq__(self, other):
        return str(self) == str(other)

    @classmethod
    def from_string(cls, string):
        return Field(*string.split('|'))

    @classmethod
    def from_class(cls, field_name):
        return Field(cls.__name__, field_name)


class FieldProperty(db.Property):
    """A property representing a Field in the datastore.
    """
    data_type = Field

    def get_value_for_datastore(self, model_instance):
        value_for_datastore = super(FieldProperty, self).get_value_for_datastore(model_instance)
        if value_for_datastore:
            return unicode(value_for_datastore)
        else:
            return None

    def make_value_from_datastore(self, value):
        if value:
            return Field.from_string(value)
        else:
            return None

    def validate(self, value):
        value = super(FieldProperty, self).validate(value)
        if value is None or isinstance(value, Field):
            return value
        elif isinstance(value, basestring):
            return Field.from_string(value)
        raise db.BadValueError("Property %s must be a Field or string." %
                               self.name)


class ModelTerms(db.Model):
    """Identifies which term values have been indexed for a given Field.
    """
    def __init__(self, parent=None, **kwargs):
        if not parent:
            parent = kwargs['model']
        super(ModelTerms, self).__init__(parent=parent, **kwargs)

    model = db.ReferenceProperty(required=True)
    field = FieldProperty(required=True)
    terms = db.StringListProperty(required=True, default=[])


class _TermInfo(db.Model):
    """Base class for Models that store Term information.
    """
    field = FieldProperty(required=True)
    weight = db.IntegerProperty(required=True)
    term_string = db.StringProperty()
    term_reference = db.ReferenceProperty()
    term_integer = db.IntegerProperty()
    term_bool = db.BooleanProperty()
    term_date = db.DateProperty()
    term_datetime = db.DateTimeProperty()


class TermUpdate(_TermInfo):
    """Represents an update (pending or processed) to a term.
    
    Pending TermUpdates have dt_processed = None
    """
    def __init__(self, parent=None, **kwargs):
        if not parent:
            parent = kwargs['model']
        super(TermUpdate, self).__init__(parent=parent, **kwargs)

    remove = db.BooleanProperty(required=True, default=False)
    model = db.ReferenceProperty(required=True)
    dt_created = db.DateTimeProperty(required=True, auto_now_add=True)
    dt_processed = db.DateTimeProperty()


class TermHits(_TermInfo):
    """Represents all models that match a specific term.
    """
    model_keys = db.ListProperty(db.Key, required=True, indexed=False)
    dt_created = db.DateTimeProperty(auto_now_add=True)
    dt_updated = db.DateTimeProperty(auto_now=True)


